<?php declare(strict_types=1);

namespace Zyucv7\Core\Web;

use Zyucv7\Core\Web\Exceptions\ResponseException;

/**
 * 安全且强大的响应处理类
 * 用于统一处理各种类型的HTTP响应
 * 
 * @author Huangkai
 */
class Response
{
    // 常用Content-Type常量
    public const CONTENT_TYPE_PLAIN = 'text/plain; charset=utf-8';
    public const CONTENT_TYPE_XML = 'application/xml; charset=utf-8';
    
    // HTTP状态码常量
    public const HTTP_OK = 200;
    public const HTTP_CREATED = 201;
    public const HTTP_NO_CONTENT = 204;
    public const HTTP_BAD_REQUEST = 400;
    public const HTTP_UNAUTHORIZED = 401;
    public const HTTP_FORBIDDEN = 403;
    public const HTTP_NOT_FOUND = 404;
    public const HTTP_METHOD_NOT_ALLOWED = 405;
    public const HTTP_INTERNAL_SERVER_ERROR = 500;
    
    /**
     * 单例实例
     * 
     * @var self|null
     */
    private static $instance = null;
    
    /**
     * HTTP状态码
     * 
     * @var int
     */
    private $statusCode = self::HTTP_OK;
    
    /**
     * 响应头信息
     * 
     * @var array
     */
    private $headers = [];
    
    /**
     * 响应内容
     * 
     * @var string
     */
    private $content = '';
    
    /**
     * 是否已发送响应
     * 
     * @var bool
     */
    private $sent = false;
    
    /**
     * 默认的安全头信息
     * 
     * @var array
     */
    private $defaultSecurityHeaders = [
        'X-Content-Type-Options' => 'nosniff',
        'X-Frame-Options' => 'DENY',
        'X-XSS-Protection' => '1; mode=block',
        'Referrer-Policy' => 'strict-origin-when-cross-origin',
        'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()',
    ];

    public static function instance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * 构造函数
     * 
     * @param int $statusCode HTTP状态码，默认为200
     * @param bool $addSecurityHeaders 是否添加安全头信息
     */
    public function __construct(int $statusCode = self::HTTP_OK, bool $addSecurityHeaders = true)
    {
        $this->statusCode = $statusCode;
        
        if ($addSecurityHeaders) {
            $this->addSecurityHeaders();
        }
    }
    
    /**
     * 克隆时重置发送状态
     */
    public function __clone()
    {
        $this->sent = false;
    }
    
    /**
     * 设置HTTP状态码
     * 
     * @param int $statusCode
     * @return self
     * @throws InvalidArgumentException 如果状态码无效
     */
    public function setStatusCode(int $statusCode): self
    {
        if ($statusCode < 100 || $statusCode >= 600) {
            throw ResponseException::InvalidArgument("Invalid HTTP status code: {$statusCode}");
        }
        
        $this->statusCode = $statusCode;
        return $this;
    }
    
    /**
     * 获取HTTP状态码
     * 
     * @return int
     */
    public function getStatusCode(): int
    {
        return $this->statusCode;
    }
    
    /**
     * 添加响应头
     * 
     * @param string $name 头名称
     * @param string $value 头值
     * @param bool $replace 是否替换已存在的头
     * @return self
     * @throws InvalidArgumentException 如果头名称或值无效
     */
    public function addHeader(string $name, string $value, bool $replace = true): self
    {
        // 验证头名称
        if (!preg_match('/^[a-zA-Z0-9\'`~!#$%&*+.^_|-]+$/', $name)) {
            throw ResponseException::InvalidArgument("Invalid header name: {$name}");
        }
        
        // 验证头值（防止CRLF注入）
        if (preg_match('/[\r\n]/', $value)) {
            throw ResponseException::InvalidArgument("Header value contains invalid characters (CRLF injection attempt)");
        }
        
        if ($replace || !isset($this->headers[$name])) {
            $this->headers[$name] = $value;
        } else {
            // 如果不替换且头已存在，可以考虑合并值（如Set-Cookie）
            if ($name === 'Set-Cookie') {
                $this->headers[] = "{$name}: {$value}";
            } else {
                $this->headers[$name] = $value;
            }
        }
        
        return $this;
    }
    
    /**
     * 批量设置响应头
     * 
     * @param array $headers 头信息数组
     * @return self
     */
    public function setHeaders(array $headers): self
    {
        foreach ($headers as $name => $value) {
            $this->addHeader($name, $value);
        }
        return $this;
    }
    
    /**
     * 获取所有响应头
     * 
     * @return array
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }
    
    /**
     * 添加安全头信息
     * 
     * @return self
     */
    public function addSecurityHeaders(): self
    {
        foreach ($this->defaultSecurityHeaders as $name => $value) {
            if (!isset($this->headers[$name])) {
                $this->headers[$name] = $value;
            }
        }
        return $this;
    }
    
    /**
     * 设置CORS头信息
     * 
     * @param string $origin 允许的源
     * @param array $methods 允许的HTTP方法
     * @param array $headers 允许的请求头
     * @param bool $credentials 是否允许凭证
     * @param int $maxAge 预检请求缓存时间
     * @return self
     */
    public function setCorsHeaders(
        string $origin = '*',
        array $methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
        array $headers = ['Content-Type', 'Authorization'],
        bool $credentials = false,
        int $maxAge = 86400
    ): self {
        $this->addHeader('Access-Control-Allow-Origin', $origin);
        $this->addHeader('Access-Control-Allow-Methods', implode(', ', $methods));
        $this->addHeader('Access-Control-Allow-Headers', implode(', ', $headers));
        $this->addHeader('Access-Control-Max-Age', (string)$maxAge);
        
        if ($credentials) {
            $this->addHeader('Access-Control-Allow-Credentials', 'true');
        }
        
        return $this;
    }
    
    /**
     * 设置响应内容为普通字符串
     * 
     * @param string $content
     * @param string $contentType 内容类型
     * @return self
     */
    public function setContent(string $content, string $contentType = self::CONTENT_TYPE_PLAIN): self
    {
        $this->content = $content;
        $this->addHeader('Content-Type', $contentType);
        return $this;
    }
    
    /**
     * 获取响应内容
     * 
     * @return string
     */
    public function getContent(): string
    {
        return $this->content;
    }
    
    /**
     * 检查响应是否已发送
     * 
     * @return bool
     */
    public function isSent(): bool
    {
        return $this->sent;
    }
    
    /**
     * 发送HTTP响应
     * 
     * @param bool $terminate 是否在发送后终止脚本执行
     * @return void
     * @throws RuntimeException 如果响应已发送
     */
    public function send(bool $terminate = false): void
    {
        if ($this->sent) {
            throw ResponseException::Runtime('Response has already been sent');
        }
        
        // 检查输出缓冲区
        if (ob_get_level() > 0) {
            ob_clean();
        }
        
        // 设置HTTP状态码
        http_response_code($this->statusCode);
        
        // 发送响应头
        foreach ($this->headers as $name => $value) {
            if (is_numeric($name)) {
                // 处理Set-Cookie等特殊情况
                header($value, false);
            } else {
                header("{$name}: {$value}", true);
            }
        }
        
        // 发送响应内容
        echo $this->content;
        
        $this->sent = true;
        
        if ($terminate) {
            exit;
        }
    }
}